package eclant.ant.types;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.Reference;
import org.eclipse.jdt.core.IClasspathContainer;

import eclant.ant.types.ClasspathEntrySelector.FileType;
import eclant.ant.types.FileSetClasspathVisitor.EntryKind;
import eclant.ant.types.FileSetClasspathVisitor.EntrySelector;

public class ClasspathFilesSelector extends AbstractPatternContainer
{
	private static final Map<String, Integer> CONTAINER_KINDS;
	static
	{
		Map<String, Integer> m = new HashMap<String, Integer>();
		m.put("application", IClasspathContainer.K_APPLICATION);
		m.put("system", IClasspathContainer.K_SYSTEM);
		m.put("default-system", IClasspathContainer.K_DEFAULT_SYSTEM);
		m.put("default_system", IClasspathContainer.K_DEFAULT_SYSTEM);
		CONTAINER_KINDS = Collections.unmodifiableMap(m);
	}

	private EntryKind matchKind = null;
	private EntryKind matchSrcKind = null;
	private FileType fileType = null;
	private String conName = null;
	private boolean hasAttributes = false;
	private int[] conKinds = null;

	/**
	 * Make this instance in effect a reference to another instance.
	 * 
	 * <p>
	 * You must not set another attribute or nest elements inside this element
	 * if you make it a reference.
	 * </p>
	 * 
	 * @param r
	 *            the <code>Reference</code> to use.
	 * @throws BuildException
	 *             if there is a problem.
	 */
	@Override
	public void setRefid(Reference r) throws BuildException
	{
		if (hasAttributes())
		{
			throw tooManyAttributes();
		}
		super.setRefid(r);
	}

	public void setKind(EntryKind includeKind)
	{
		gotAttribute();
		if (fileType != null && !includeKind.equals(EntryKind.lib))
			throw new BuildException("The \"type\" attribute should only be used with kind=\"lib\"");
		this.matchKind = includeKind;
	}

	public void setType(FileType fileType)
	{
		gotAttribute();
		if (matchKind != null && !matchKind.equals(EntryKind.lib))
			throw new BuildException("The \"type\" attribute should only be used with kind=\"lib\"");
		this.fileType = fileType;
	}

	public void setSrckind(EntryKind includeKind)
	{
		gotAttribute();
		this.matchSrcKind = includeKind;
	}

	public void setContainer(String name)
	{
		gotAttribute();
		this.conName = ("*".equals(name) ? null : name);
	}

	public void setContainerKinds(String kinds)
	{
		gotAttribute();
		if (kinds == null || kinds.trim().equals(""))
			this.conKinds = null;
		else
		{
			String[] arr = kinds.split("\\s+");
			List<Integer> values = new ArrayList<Integer>();
			for (String s : arr)
			{
				if ("".equals(s))
					continue;
				Integer val = CONTAINER_KINDS.get(s);
				if (val == null)
					try
					{
						val = Integer.valueOf(s);
					}
					catch (NumberFormatException ex)
					{
						throw new BuildException(String.format("Invalid container kind \"%s\"", s),
								getLocation());
					}
				values.add(val);
			}
			conKinds = new int[values.size()];
			for (int i = 0; i < conKinds.length; i++)
				conKinds[i] = values.get(i).intValue();
		}
	}

	public EntrySelector getEntrySelector()
	{
		if (isReference())
			return getRef().getEntrySelector();
		validate();
		ResourceSelector rs = null;
		if (hasPatterns() || hasSelectors())
		{
			rs = new ResourceSelector();
			setupResourceSelector(rs);
		}
		return new ClasspathEntrySelector(matchKind, matchSrcKind, fileType, conName, conKinds, rs);
	}

	@Override
	public void validate()
	{
		if (!hasAttributes())
			throw new BuildException("No selection criteria has been specified", getLocation());
		super.validate();
	}

	@Override
	public String toString()
	{
		StringBuilder sb = new StringBuilder("select [");
		String sep = "";
		if (matchKind != null)
		{
			sb.append(sep).append("kind=").append(matchKind);
			sep = ", ";
		}
		if (matchSrcKind != null)
		{
			sb.append(sep).append("srckind=").append(matchSrcKind);
			sep = ", ";
		}
		if (conName != null)
		{
			sb.append(sep).append("container=").append(conName);
			sep = ", ";
		}
		if (conKinds != null && conKinds.length > 0)
		{
			sb.append(sep).append("containerKinds=[");
			sep = "";
			for (int i : conKinds)
			{
				sb.append(sep).append(String.valueOf(i));
				sep = ", ";
			}
			sb.append("]");
		}
		sb.append("], ").append(super.toString());
		return sb.toString();
	}

	@Override
	protected ClasspathFilesSelector getRef()
	{
		return (ClasspathFilesSelector) getCheckedRef();
	}


	protected boolean hasAttributes()
	{
		return hasAttributes;
	}

	protected void gotAttribute()
	{
		checkAttributesAllowed();
		hasAttributes = true;
	}
}
